//
// Created by 77873 on 2020/7/16 0016.
//


#include "MineAudio.h"


MineAudio::~MineAudio() {
    release();
}

MineAudio::MineAudio(MinePlayStatus *status, MineCallJava *callJava, int sample_Reate) {
    this->playStatus = status;
    this->callJava = callJava;
    this->sample_Reate = sample_Reate;
    mineQueue = new MineQueue(playStatus);
    buffer = (uint8_t *) (av_malloc(sample_Reate * 2 * 2));

    this->isCut= false;
    this->end_time=0;
    this->showPcm= false;

    //soundtouch 初始化
    sampleBuffer = static_cast<SAMPLETYPE *>(malloc(sample_Reate * 2 * 2));
    soundTouch = new SoundTouch();
    soundTouch->setSampleRate(sample_Reate);
    soundTouch->setChannels(2);
    soundTouch->setPitch(pitch);
    soundTouch->setTempo(speed);

}

int MineAudio::getSoundTouchData() {
    while (playStatus != NULL && !playStatus->exit) {
        out_buffer=NULL;
        if (finished) {
            finished= false;
            data_size = resampleAudio(reinterpret_cast<void **>(&out_buffer));
            if (data_size > 0) {
                for (int i = 0; i < data_size / 2 + 1; ++i) {
                    sampleBuffer[i] = (out_buffer[i * 2] | ((out_buffer[i * 2 + 1]) << 8));
                }
                soundTouch->putSamples(sampleBuffer,nb);
                num=soundTouch->receiveSamples(sampleBuffer,data_size/4);
            } else{
                soundTouch->flush();
            }
        }
        if(num==0){
            finished= true;
            continue;
        } else{
            if(out_buffer==NULL){
                num=soundTouch->receiveSamples(sampleBuffer,data_size/4);
                if(num==0){
                finished= true;
                    continue;
                }
            }
            return num;
        }
    }
    return 0;
}


void *decodePlay(void *data) {
//    LOGE("MineAudio decodePlay");

    MineAudio *mineAudio = (MineAudio *) (data);
    /**
     * 使用OpenSL ES播放pcm数据时，调用初始化
     */
//    mineAudio->resampleAudio();

    mineAudio->initOpenSL();

    pthread_exit(&mineAudio->threadPlay);
}

/**
 * 重采样输出文件
 */
//FILE *outFile = fopen("/storage/emulated/0/ffmpeg/resampleMusic.pcm", "w");

void *pcmCallBack(void *data) {
    LOGE("线程回调函数  pcmCallBack");

    MineAudio *mineAudio = (MineAudio *) (data);

    mineAudio->bufferQueue=new WlBufferQueue(mineAudio->playStatus);
    
    while(mineAudio->playStatus!=NULL&&!mineAudio->playStatus->exit){
        PcmDataBean *pcmDataBean=NULL;
        mineAudio->bufferQueue->getBuffer(&pcmDataBean);
        if(pcmDataBean==NULL){
            continue;
        }
        LOGE("pcmbean buffer size is %d",pcmDataBean->buffsize);
        if(pcmDataBean->buffsize<=mineAudio->defaultPcmSize){//不用分包
            if(mineAudio->isRecordPcm){
                mineAudio->callJava->OnCallPcmToAAC(CHILD_THREAD,pcmDataBean->buffsize,pcmDataBean->buffer);
            }
            if(mineAudio->showPcm){
                mineAudio->callJava->onPcmCallBack(pcmDataBean->buffer,pcmDataBean->buffsize);
            }
        } else{
            //分包
            int pack_num=pcmDataBean->buffsize/mineAudio->defaultPcmSize;
            int pack_sub=pcmDataBean->buffsize%mineAudio->defaultPcmSize;

            for (int i = 0; i < pack_num; ++i) {
                char*bf= static_cast<char *>(malloc(mineAudio->defaultPcmSize));
                memcpy(bf,pcmDataBean->buffer+i*mineAudio->defaultPcmSize,mineAudio->defaultPcmSize);

                if(mineAudio->isRecordPcm){
                    mineAudio->callJava->OnCallPcmToAAC(CHILD_THREAD,mineAudio->defaultPcmSize,bf);
                }
                if(mineAudio->showPcm){
                    mineAudio->callJava->onPcmCallBack(bf,mineAudio->defaultPcmSize);
                }
                bf=NULL;
            }

            if(pack_sub>0){
                char *bf= static_cast<char *>(malloc(pack_sub));
                memcpy(bf,pcmDataBean->buffer+pack_num*mineAudio->defaultPcmSize,pack_sub);

                if(mineAudio->isRecordPcm){
                    mineAudio->callJava->OnCallPcmToAAC(CHILD_THREAD,pack_sub,bf);
                }
                if(mineAudio->showPcm){
                    mineAudio->callJava->onPcmCallBack(bf,pack_sub);
                }
            }
        }
        delete(pcmDataBean);
        pcmDataBean=NULL;
    }
    pthread_exit(&mineAudio->pcmCallBackThread);
}


void MineAudio::play() {
    pthread_create(&threadPlay, NULL, decodePlay, this);
    pthread_create(&pcmCallBackThread,NULL,pcmCallBack,this);
}

int MineAudio::resampleAudio(void **pcmbuf) {
    /**
     * 循环读取avPacket
     */

//    LOGE("MineAudio::resampleAudio");


    while (playStatus != NULL && !playStatus->exit) {
//        LOGE("MineAudio::while");

        if(playStatus->seek){
            av_usleep(1000*100);
            continue;
        }

        if (mineQueue->getQueueSize() == 0)//加载中
        {
            if (!playStatus->load) {
                playStatus->load = true;
                callJava->onCallLoad(CHILD_THREAD, true);
            }
            av_usleep(1000*100);
            continue;
        } else {
            if (playStatus->load) {
                playStatus->load = false;
                callJava->onCallLoad(CHILD_THREAD, false);
            }
        }

        //如果一帧含有多个 avpacket
        if(readFrameFinished){
            avPacket = av_packet_alloc();
            if (mineQueue->outAvpacket(avPacket) != 0) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                continue;
            }
//        LOGE("outAvpacket");
            ret = avcodec_send_packet(avCodecContext, avPacket);

            if (ret != 0) {
                if (LOG_DEBUG) {
                    LOGE("avcodec_send_packet on error");
                }

                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                continue;
            }
        }


//        LOGE("avcodec_send_packet");

        //为avFrame 开辟空间
        avFrame = av_frame_alloc();
        ret = avcodec_receive_frame(avCodecContext, avFrame);

        //读取成功后，获取到一个 avFrame
        if (ret == 0) {
            readFrameFinished= false;
            /**
             * 有声道数 但是声道布局为0
             * 设置声道布局
             */
            if (avFrame->channels > 0 && avFrame->channel_layout == 0) {
                avFrame->channel_layout = av_get_default_channel_layout(avFrame->channels);

            } else if (avFrame->channels == 0 && avFrame->channel_layout > 0) {
                avFrame->channels = av_get_channel_layout_nb_channels(avFrame->channel_layout);
            }

            //重采样上下文
            SwrContext *swrContext = NULL;

            /**
             * struct SwrContext *s,
             * int64_t out_ch_layout,   //输出声道布局
             * enum AVSampleFormat out_sample_fmt,// 采样位数
             * int out_sample_rate,
             * int64_t  in_ch_layout,
             * enum AVSampleFormat  in_sample_fmt,
             * int  in_sample_rate,
             * int log_offset,
             * void *log_ctx
             */

            swrContext = swr_alloc_set_opts(NULL,
                                            AV_CH_LAYOUT_STEREO,
                                            AV_SAMPLE_FMT_S16,
                                            avFrame->sample_rate,
                                            avFrame->channel_layout,
                                            static_cast<AVSampleFormat>(avFrame->format),
                                            avFrame->sample_rate,
                                            NULL, NULL
            );
            if (LOG_DEBUG) {
//                LOGE("swr_alloc_set_opts   ");
            }

            if (!swrContext ||
                swr_init(swrContext) < 0) {//如果失败进行初始化
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;

                av_frame_free(&avFrame);
                av_free(avFrame);
                avFrame = NULL;

                swr_free(&swrContext);
                readFrameFinished= true;
                continue;
            }

            if (LOG_DEBUG) {
//                LOGE("swr_convert   ");
            }
            nb = swr_convert(swrContext,
                                 &buffer,   //输出buffer
                                 avFrame->nb_samples,
                                 (const uint8_t **) (avFrame->data),
                                 avFrame->nb_samples
            );

            int outChannels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);

            data_size = nb * outChannels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);

            /**
             * 获取当前frame的时间
             */
            now_time = avFrame->pts * av_q2d(time_base);
            if (now_time < clock) {
                now_time = clock;
            }
            clock = now_time;


            if (LOG_DEBUG) {
//                LOGE("fwrite 开始写入数据到文件 ");
            }
//            fwrite(buffer, 1, data_size, outFile);

            if (LOG_DEBUG) {
//                LOGE("data  size is I%d", data_size);
            }
            *pcmbuf = buffer;


               // av_packet_free(&avPacket);
               // av_free(avPacket);
               // avPacket = NULL;

            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
            swr_free(&swrContext);
            swrContext = NULL;
            break;
        } else {
            if (LOG_DEBUG) {
//                LOGE("avcodec_receive_frame on error");
            }
            readFrameFinished= true;
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;

            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
            continue;
        }
    }
//    fclose(outFile);
    return data_size;
}

void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void *context) {
//    LOGE("MineAudio pcmBufferCallBack");

    MineAudio *mineAudio = (MineAudio *) (context);
//    LOGE("%s", "pcmBufferCallBack  ");
    if (mineAudio != NULL) {
        int buffersize = mineAudio->getSoundTouchData();
        if (buffersize > 0) {
            (*mineAudio).clock += buffersize / ((double) (mineAudio->sample_Reate * 2 * 2));

            if (mineAudio->clock - mineAudio->last_time >= 0.1) {
                //回调到应用层
                mineAudio->last_time = mineAudio->clock;
                (*mineAudio).callJava->onCallTimeInfo(CHILD_THREAD, mineAudio->clock,
                                                      mineAudio->duration);
            }
           //    if(mineAudio->isRecordPcm){
           //        mineAudio->callJava->OnCallPcmToAAC(CHILD_THREAD,buffersize*2*2,mineAudio->sampleBuffer);
           //    }
           
           mineAudio->bufferQueue->putBuffer(mineAudio->sampleBuffer,buffersize*2*2);

            mineAudio->callJava->onCallValumeDB(CHILD_THREAD,
                                                mineAudio->getPCMDB(reinterpret_cast<char *>(mineAudio->sampleBuffer), buffersize * 4));


            (*mineAudio->pcmBufferQueue)->Enqueue(mineAudio->pcmBufferQueue,
                                                  (char *) mineAudio->sampleBuffer,
                                                  buffersize*2*2);

            if(mineAudio->isCut){
                if(mineAudio->clock>mineAudio->end_time){
                    LOGE("裁减退出...")
                    mineAudio->playStatus->exit= true;
                }
            }
        }
    } else {
        LOGE("%s", "pcmBufferCallBack  mineAudio  is null");
    }
}


void MineAudio::createEngine() {

//    LOGE("MineAudio createEngine");
    SLresult result;

    // create engine
    result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
    if (SL_RESULT_SUCCESS != result) {

        LOGE("slCreateEngine  on error");
        return;
    };

    // realize the engine
    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("realize the engine  on  error");
        return;
    }
    //get the engine interface, which is needed in order to create other objects
    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &enginEngine);

    if (result != SL_RESULT_SUCCESS) {
        LOGE("get the engine interface  on error");
        return;
    }


    //create output mix, with environmental reverb specified as a non-required interface

    const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};
    const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};


    result = (*enginEngine)->CreateOutputMix(enginEngine, &outputMixObject, 1, mids, mreq);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("CreateOutputMix  on  error")
        return;
    }

    //realize the output mix
    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("realize the output mix  on error");
        return;
    }
    // get the environmental reverb interface
    result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,
                                              &outputMixEnviromentalReverb);

    if (result != SL_RESULT_SUCCESS) {
        LOGE("get the environmental reverb interface  on error")
        return;
    }

    result = (*outputMixEnviromentalReverb)->SetEnvironmentalReverbProperties(
            outputMixEnviromentalReverb, &reverbSettings);

    if (result != SL_RESULT_SUCCESS) {
        LOGE("SetEnvironmentalReverbProperties  on error ");
        return;
    }
}

void MineAudio::createBufferQueueAudioPlayer() {
//    LOGE("MineAudio createBufferQueueAudioPlayer");

    SLresult result;
    SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
    SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,
                                   2,//声道数
            //SL_SAMPLINGRATE_44_1,//采样率
                                   static_cast<SLuint32>(getCurrentSampleRateForOpensles(
                                           sample_Reate)),//44100hz的频率
                                   SL_PCMSAMPLEFORMAT_FIXED_16,
                                   SL_PCMSAMPLEFORMAT_FIXED_16,
                                   SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
                                   SL_BYTEORDER_LITTLEENDIAN};
    SLDataSource audioSrc = {&loc_bufq, &format_pcm};

    // configure audio sink
    SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
    SLDataSink audioSnk = {&loc_outmix, NULL};


    const SLInterfaceID ids[4] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_MUTESOLO,SL_IID_PLAYBACKRATE};
    const SLboolean req[4] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,SL_BOOLEAN_TRUE};


    result = (*enginEngine)->CreateAudioPlayer(enginEngine,
                                               &pcmPlayerObject, &audioSrc, &audioSnk,
                                               4, ids,
                                               req);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("CreateAudioPlayer  on error")
        return;
    }

    // realize the player
    result = (*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("realize the player  on error")
        return;
    }
    // get the play interface
    result = (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("get the play interface  on error")
        return;
    }
    // get the buffer queue interface
    result = (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("get the buffer queue interface  on error")
        return;
    }
    //获取声道接口

    result = (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_MUTESOLO, &pcmMutePlay);

    //获取声道接口
    if (result != SL_RESULT_SUCCESS) {
        LOGE("get the SLMuteSoloItf on error");
        return;
    }

    /**
     * 获取声音大小
     */
    result = (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &slVolumeItf);
    setVolume(volumePercent);
    setMute(mute);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("get the slVolumeItf on error");
        return;
    }


    // register callback on the buffer queue
    result = (*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, this);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("register callback on the buffer queue  on error")
        return;
    }

    result = (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("SetPlayState  on error")
        return;
    }
    /**
     * 回调
     */
    pcmBufferCallBack(pcmBufferQueue, this);
}

void MineAudio::initOpenSL() {
//    LOGE("MineAudio initOpenSL");
    createEngine();

    createBufferQueueAudioPlayer();
}


void MineAudio::pause() {

    (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PAUSED);
}

void MineAudio::resume() {

    (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
}

int MineAudio::getCurrentSampleRateForOpensles(int sample_rate) {

    int rate = 0;
    switch (sample_rate) {
        case 8000:
            rate = SL_SAMPLINGRATE_8;
            break;
        case 11025:
            rate = SL_SAMPLINGRATE_11_025;
            break;
        case 12000:
            rate = SL_SAMPLINGRATE_12;
            break;
        case 16000:
            rate = SL_SAMPLINGRATE_16;
            break;
        case 22050:
            rate = SL_SAMPLINGRATE_22_05;
            break;
        case 24000:
            rate = SL_SAMPLINGRATE_24;
            break;
        case 32000:
            rate = SL_SAMPLINGRATE_32;
            break;
        case 44100:
            rate = SL_SAMPLINGRATE_44_1;
            break;
        case 48000:
            rate = SL_SAMPLINGRATE_48;
            break;
        case 64000:
            rate = SL_SAMPLINGRATE_64;
            break;
        case 88200:
            rate = SL_SAMPLINGRATE_88_2;
            break;
        case 96000:
            rate = SL_SAMPLINGRATE_96;
            break;
        case 192000:
            rate = SL_SAMPLINGRATE_192;
            break;
        default:
            rate = SL_SAMPLINGRATE_44_1;
    }
    return rate;
}

void MineAudio::stop() {
    if (LOG_DEBUG) {
        LOGE("MineAudio::stop()");
    }
    if (NULL != pcmPlayerPlay) {
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_STOPPED);
    }
}

/**
 * 设置声音大小
 * @param percent
 */
void MineAudio::setVolume(int percent) {
    volumePercent = percent;
    if (NULL != slVolumeItf) {

        if (percent > 30) {
            (*slVolumeItf)->SetVolumeLevel(slVolumeItf, (100 - percent) * -20);
        } else if (percent > 25) {
            (*slVolumeItf)->SetVolumeLevel(slVolumeItf, (100 - percent) * -22);
        } else if (percent > 20) {
            (*slVolumeItf)->SetVolumeLevel(slVolumeItf, (100 - percent) * -25);
        } else if (percent > 15) {
            (*slVolumeItf)->SetVolumeLevel(slVolumeItf, (100 - percent) * -28);
        } else if (percent > 10) {
            (*slVolumeItf)->SetVolumeLevel(slVolumeItf, (100 - percent) * -30);
        } else if (percent > 5) {
            (*slVolumeItf)->SetVolumeLevel(slVolumeItf, (100 - percent) * -34);
        } else if (percent > 3) {
            (*slVolumeItf)->SetVolumeLevel(slVolumeItf, (100 - percent) * -37);
        } else if (percent > 0) {
            (*slVolumeItf)->SetVolumeLevel(slVolumeItf, (100 - percent) * -40);
        } else {
            (*slVolumeItf)->SetVolumeLevel(slVolumeItf, (100 - percent) * -100);
        }

    }
}


void MineAudio::release() {
    if (LOG_DEBUG) {
        LOGE("MineAudio::release()");
    }
//    stop();

    if(bufferQueue!=NULL){
        bufferQueue->noticeThread();
        pthread_join(pcmCallBackThread,NULL);
        bufferQueue->release();
        delete(bufferQueue);
        bufferQueue=NULL;
    }


    if (mineQueue != NULL) {
        delete (mineQueue);
        mineQueue = NULL;
    }
/*释放OpenSL ES*/
    if (NULL != pcmPlayerObject) {
        (*pcmPlayerObject)->Destroy(pcmPlayerObject);
        pcmPlayerObject = NULL;
        pcmPlayerPlay = NULL;
        pcmBufferQueue = NULL;
        slVolumeItf = NULL;
        pcmMutePlay=NULL;

    }
    if (NULL != outputMixObject) {
        (*outputMixObject)->Destroy(outputMixObject);
        outputMixObject = NULL;
        outputMixEnviromentalReverb = NULL;
    }

    if (NULL != engineObject) {
        (*engineObject)->Destroy(engineObject);
        engineObject = NULL;
        enginEngine = NULL;
    }

    //释放buffer  buffer是我们自己 malloc  申请的堆空间  需要手动释放
    if (buffer != NULL) {
        free(buffer);
        buffer = NULL;
    }

    if(NULL!=out_buffer){
        out_buffer=NULL;
    }
    if(NULL!=soundTouch){
        delete  soundTouch;
        soundTouch=NULL;
    }
    if(NULL!=sampleBuffer){
        free(sampleBuffer);
        sampleBuffer=NULL;
    }

    /**释放解码器上下文*/
    if (avCodecContext != NULL) {
        avcodec_close(avCodecContext);
        avcodec_free_context(&avCodecContext);
        avCodecContext = NULL;
    }

    if (playStatus != NULL) {
        playStatus = NULL;
    }
    if (callJava != NULL) {
        callJava = NULL;
    }

}

void MineAudio::setMute(int mute) {
    this->mute = mute;
    if (NULL != pcmMutePlay) {
        if (mute == 0) {
            (*pcmMutePlay)->SetChannelMute(pcmMutePlay, 1, false);
            (*pcmMutePlay)->SetChannelMute(pcmMutePlay, 0, true);
        } else if (mute == 1) {
            (*pcmMutePlay)->SetChannelMute(pcmMutePlay, 1, true);
            (*pcmMutePlay)->SetChannelMute(pcmMutePlay, 0, false);

        } else if (mute == 2) {
            (*pcmMutePlay)->SetChannelMute(pcmMutePlay, 1, false);
            (*pcmMutePlay)->SetChannelMute(pcmMutePlay, 0, false);
        }
    }

}

void MineAudio::setPitch(float pitch) {
    this->pitch=pitch;
    if(NULL!=soundTouch){
        soundTouch->setPitch(pitch);
    }
}

void MineAudio::setSpeed(float speed) {
    this->speed=speed;
    if(NULL!=soundTouch){
        soundTouch->setTempo(speed);
    }
}

int MineAudio::getPCMDB(char *pcmdata, size_t pcmsize) {
    int db = 0;
    short int pervalue = 0;
    double sum = 0;
    for(int i = 0; i < pcmsize; i+= 2)
    {
        memcpy(&pervalue, pcmdata+i, 2);
        sum += abs(pervalue);
    }
    sum = sum / (pcmsize / 2);
    if(sum > 0)
    {
        db = (int)20.0 *log10(sum);
    }
    return db;
}

void MineAudio::startStopRecord(bool start) {
    this->isRecordPcm=start;
}





